package com.harmonyos.cases.view;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.harmonyos.cases.bean.*;
import com.harmonyos.cases.bean.code.ETSMethodInfo;
import com.harmonyos.cases.bean.code.ExtraInfo;
import com.harmonyos.cases.bean.repository.CategoryInfo;
import com.harmonyos.cases.bean.repository.RepositoriesInfo;
import com.harmonyos.cases.bean.repository.Result;
import com.harmonyos.cases.bean.repository.TypeInfo;
import com.harmonyos.cases.constants.Constants;
import com.harmonyos.cases.listener.CodeListener;
import com.harmonyos.cases.listener.ImageLoaderListener;
import com.harmonyos.cases.utils.*;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField;
import com.intellij.ui.jcef.JBCefBrowser;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.*;

import static com.harmonyos.cases.action.DemoTestAction.toolWindowController;
import static com.harmonyos.cases.constants.Constants.TEST_SETTING;
import static com.harmonyos.cases.utils.InsertUtils.*;

// 插件页面Dialog
public class PluginDialog extends DialogWrapper {
    // 分类列表
    private final List<TypeInfo> typeInfoList = new ArrayList<>();
    // 类型列表
    private final List<CategoryInfo> categoryInfoList = new ArrayList<>();
    // 被选中的分类（源代码、三方库）
    private String selectedCategory = "";
    // 被选择的类型（动效、UI、其他等）
    private String selectedType = "";
    // key：分类，value：该分类下的类型列表
    private final Map<String, List<TypeInfo>> categoryMap = new HashMap<>();
    // key：类型，value：该类型下的案例列表
    private final Map<String, Map<String, List<RepositoriesInfo>>> typeMap = new HashMap<>();
    // 类型简介组件
    private JTextPane typeDesTextArea;

    // 被选中的item位置
    private int selectPosition = -1;
    // 数据列表
    private final List<RepositoriesInfo> dataList = new ArrayList<>();

    private CodeListener codeListener;
    // 描述组件
    private JTextPane desLabel;

    DefaultListModel<TypeInfo> defaultListModel;
    // 图片下载线程
    private ImageLoaderWorker imageLoaderWorker;
    // 组件列表
    private JPanel componentPanel;
    // loading提示
    private JLabel loadingText;
    private JPanel loadingPanel;
    // Grid列表滑动组件
    private JBScrollPane jGridScrollPane;
    // 本地测试配置
    private String localConfigUlr = Constants.CONFIG_PATH;
    private String localOwner = Constants.GITEE_OWNER;
    private String localRepo = Constants.GITEE_REPO;
    // git分支
    public static String localRef = "";

    private SettingBean localSetting = null;
    // 浏览器组件，用于百度统计
    private JBCefBrowser browser1;
    private JBCefBrowser browser2;
    // 滑动组件
    private JBScrollPane desScrollPane;

    private boolean useLocalConfig = true;
    private boolean isDataLoadSuccess = true;

    public void setCodeListener(CodeListener codeListener) {
        this.codeListener = codeListener;
    }

    public PluginDialog(CodeListener codeListener) {
        super(true); // use current window as parent
        this.codeListener = codeListener;

        // 设置插件名
        setTitle(Constants.PLUGIN_TITLE);
        // 如果是测试版本，则加载测试配置文件，将config地址换为自己配置的路径
        if (Constants.IS_DEBUG) {
            try {
                if (Files.exists(Path.of(TEST_SETTING))) {
                    String localSettingString = Files.readString(Path.of(TEST_SETTING));
                    if (localSettingString != null && !localSettingString.isEmpty()) {
                        localSetting = new ObjectMapper().readValue(localSettingString, SettingBean.class);
                        localConfigUlr = localSetting.getConfigUrl();
                        localOwner = localSetting.getOwner();
                        localRepo = localSetting.getRepo();
                        localRef = localSetting.getRef();
                        Constants.HOME_URL = String.format(Constants.BASE_URL, localOwner, localRepo);
                    }
                }
            } catch (IOException e) {
//                throw new RuntimeException(e);
            }
        }

        requestData();
        // 初始化 UI
        init();
    }

    /**
     * 请求在线数据
     */
    public void requestData() {
        String fileName = getFileNameFromUrl(localConfigUlr);
        DataReader dataReader = new DataReader();

        // 测试代码，读取本地配置，用于本地测试，后续会应用到缓存功能中
//        if (useLocalConfig) {
//            try {
//                // 读取本地数据
//                File localFile = new File(getCacheDirectory(), fileName);
//                String config = localData(localFile);
//                // 解析本地数据
//                ObjectMapper mapper = new ObjectMapper();
//                List<Result> resultList = mapper.readValue(config, new TypeReference<>() {
//                });
//                formatData(resultList);
//            } catch (JsonProcessingException ex) {
//                throw new RuntimeException("本地数据解析失败");
//            }
//            return;
//        }
        // 读取配置文件，先读取在线，如果在线有问题，则读取本地保存的，如果本地保存的有问题，则读取插件中自带的插件，防止报错
        try {
            // 格式化数据
            formatData(dataReader.onlineData(localConfigUlr, fileName));
        } catch (RuntimeException | IOException e) {
            System.out.println("在线数据加载异常：" + e.getMessage());
            // 如果在线数据出现错误，则加载本地数据
            try {
                // 读取本地数据
                formatData(dataReader.localData(fileName));
            } catch (RuntimeException | IOException ex) {
                System.out.println("本地数据加载异常：" + ex.getMessage());
                try {
                    formatData(dataReader.packageData());
                } catch (IOException exception) {
                    System.out.println("包数据加载异常：" + exception.getMessage());
                }
            }
        }
    }

    // 格式化代码
    private void formatData(List<Result> resultList) {
        if (resultList == null) {
            isDataLoadSuccess = false;
            System.out.println("插件数据获取失败");
            return;
        }
        for (Result result : resultList) {
            categoryInfoList.add(result.getCategory());
            List<TypeInfo> typeInfos = new ArrayList<>();
            TypeInfo typeAll = new TypeInfo();
            typeAll.setTypeName("all");
            typeAll.setTypeDesc("全部");
            typeInfos.add(typeAll);
            Map<String, List<RepositoriesInfo>> tempMap = new HashMap<>();
            tempMap.put(typeAll.getTypeName(), new ArrayList<>());
            for (int i = 0; i < result.getCategoryDetailList().size(); i++) {
                typeInfos.add(result.getCategoryDetailList().get(i).getTypeInfo());
                List<RepositoriesInfo> tempList = result.getCategoryDetailList().get(i).getRepositoriesInfoList();
                tempMap.get(typeAll.getTypeName()).addAll(tempList);
                tempMap.put(result.getCategoryDetailList().get(i).getTypeInfo().getTypeName(), tempList);
            }
            typeMap.put(result.getCategory().getCategoryName(), tempMap);
            categoryMap.put(result.getCategory().getCategoryName(), typeInfos);
        }

        selectedCategory = categoryInfoList.get(0).getCategoryName();
        typeInfoList.clear();
        typeInfoList.addAll(categoryMap.get(selectedCategory));
    }

    @Override
    protected JComponent createSouthPanel() {
        // Dialog下方布局
        JPanel southPanel = new JPanel();
        southPanel.setPreferredSize(new Dimension(1000, 40));
        SpringLayout springLayout = new SpringLayout();
        southPanel.setLayout(springLayout);

        // 问卷调查
        JTextPane linkPane = new JTextPane();
        // “问卷调查”添加蓝色链接
        SimpleAttributeSet simpleAttributeSet = new SimpleAttributeSet();
        StyleConstants.setForeground(simpleAttributeSet, JBColor.BLUE);
        StyleConstants.setUnderline(simpleAttributeSet, true);
        StyledDocument styledDocument = linkPane.getStyledDocument();
        try {
            styledDocument.insertString(styledDocument.getLength(), Constants.QUESTIONNAIRE_TEXT, null);
            styledDocument.setCharacterAttributes(45, 4, simpleAttributeSet, false);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        // 禁止编辑
        linkPane.setEditable(false);
        linkPane.setDisabledTextColor(new JBColor(new Color(0, 0, 0, 0), new Color(0, 0, 0, 0)));
        // 设置padding
        linkPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        // 设置字体大小
        linkPane.setFont(linkPane.getFont().deriveFont(12f));
        springLayout.putConstraint(SpringLayout.WEST, linkPane, 0, SpringLayout.WEST, southPanel);
        springLayout.putConstraint(SpringLayout.NORTH, linkPane, 10, SpringLayout.NORTH, southPanel);
        springLayout.putConstraint(SpringLayout.SOUTH, linkPane, 0, SpringLayout.SOUTH, southPanel);
        // “问卷调查”点击事件
        linkPane.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                try {
                    // 通过浏览器打开问卷调查链接
                    Desktop.getDesktop().browse(new URI(Constants.QUESTIONNAIRE_URL));
                } catch (IOException | URISyntaxException ex) {
                    throw new RuntimeException(ex);
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                // 改变鼠标光标状态
                linkPane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            }

            @Override
            public void mouseExited(MouseEvent e) {
                // 改变鼠标光标状态
                linkPane.setCursor(Cursor.getDefaultCursor());
            }
        });
        southPanel.add(linkPane);
        // 取消按钮
        JButton cancelButton = new JButton("取消");
        cancelButton.setFont(cancelButton.getFont().deriveFont(14f));
        springLayout.putConstraint(SpringLayout.EAST, cancelButton, 0, SpringLayout.EAST, southPanel);
        springLayout.putConstraint(SpringLayout.NORTH, cancelButton, 5, SpringLayout.NORTH, southPanel);
        springLayout.putConstraint(SpringLayout.SOUTH, cancelButton, 0, SpringLayout.SOUTH, southPanel);
        cancelButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                cancelButton.requestFocus();
            }

            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                cancelButton.requestFocus();
                doCancelAction();
            }
        });
        southPanel.add(cancelButton);
        // 确定按钮
        JButton okButton = new JButton("确定");
        okButton.setFont(cancelButton.getFont().deriveFont(14f));
        springLayout.putConstraint(SpringLayout.EAST, okButton, -20, SpringLayout.WEST, cancelButton);
        springLayout.putConstraint(SpringLayout.NORTH, okButton, 5, SpringLayout.NORTH, southPanel);
        springLayout.putConstraint(SpringLayout.SOUTH, okButton, 0, SpringLayout.SOUTH, southPanel);
        okButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                okButton.requestFocus();
            }

            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                okButton.requestFocus();
                doOKAction();
            }
        });
        southPanel.add(okButton);

        return southPanel;
    }

    @Override
    protected @Nullable JComponent createCenterPanel() {

        // 初始化整体父组件
        JPanel panel = new JPanel();
        panel.setPreferredSize(new Dimension(1000, 800));
        panel.setMinimumSize(new Dimension(1000, 800));
        panel.setMaximumSize(new Dimension(1000, 800));
        SpringLayout springLayout = new SpringLayout();
        panel.setLayout(springLayout);
        panel.setBorder(BorderFactory.createEmptyBorder());
        panel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                e.getComponent().requestFocus();
            }
        });

        // 初始化title
        JLabel titlePane = new JLabel();
        titlePane.setText("");
        springLayout.putConstraint(SpringLayout.WEST, titlePane, 10, SpringLayout.WEST, panel);
        panel.add(titlePane);
        // 副标题
        JLabel subtitlePane = new JLabel();
        subtitlePane.setText(Constants.PLUGIN_SUB_TITLE);
        springLayout.putConstraint(SpringLayout.WEST, subtitlePane, 10, SpringLayout.WEST, panel);
        springLayout.putConstraint(SpringLayout.NORTH, subtitlePane, 10, SpringLayout.NORTH, panel);
        subtitlePane.setFont(subtitlePane.getFont().deriveFont(Font.BOLD, 16f));
        panel.add(subtitlePane);

        // 初始化类型列表
        JBList<TypeInfo> typeList = getTypeList();
        springLayout.putConstraint(SpringLayout.NORTH, typeList, 20, SpringLayout.SOUTH, subtitlePane);
        springLayout.putConstraint(SpringLayout.WEST, typeList, 20, SpringLayout.WEST, panel);
        panel.add(typeList);

        // 竖向分割线
        JSeparator separatorVertical = new JSeparator(SwingConstants.VERTICAL);
        separatorVertical.setBackground(JBColor.DARK_GRAY);
        separatorVertical.setPreferredSize(new Dimension(2, Integer.MAX_VALUE));
        springLayout.putConstraint(SpringLayout.WEST, separatorVertical, 0, SpringLayout.EAST, typeList);
        springLayout.putConstraint(SpringLayout.NORTH, separatorVertical, 10, SpringLayout.SOUTH, subtitlePane);
        panel.add(separatorVertical);

        // 横向分割线
        JSeparator separatorHorizontal = new JSeparator(SwingConstants.HORIZONTAL);
        separatorHorizontal.setPreferredSize(new Dimension(Integer.MAX_VALUE, 2));
        springLayout.putConstraint(SpringLayout.SOUTH, separatorHorizontal, 0, SpringLayout.SOUTH, panel);
        panel.add(separatorHorizontal);
        // 分类选择Spinner
        ComboBox<CategoryInfo> categoryComboBox = new ComboBox<>();
        for (CategoryInfo categoryInfo : categoryInfoList) {
            categoryComboBox.addItem(categoryInfo);
        }
        categoryComboBox.setFont(categoryComboBox.getFont().deriveFont(14f));
        springLayout.putConstraint(SpringLayout.WEST, categoryComboBox, 20, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.NORTH, categoryComboBox, -5, SpringLayout.NORTH, separatorVertical);
        categoryComboBox.setPreferredSize(new Dimension(100, 34));
        categoryComboBox.addItemListener(itemEvent -> {
            if (itemEvent.getStateChange() == ItemEvent.SELECTED) {
                selectedCategory = ((CategoryInfo) itemEvent.getItem()).getCategoryName();
                defaultListModel.clear();
                defaultListModel.addAll(categoryMap.get(selectedCategory));
                typeList.setModel(defaultListModel);
                typeList.setSelectedIndex(0);
            }
        });
        panel.add(categoryComboBox);

        JLabel importTypeLabel = new JLabel();
        importTypeLabel.setFont(importTypeLabel.getFont().deriveFont(14f));
        importTypeLabel.setText("引入方式");
        springLayout.putConstraint(SpringLayout.EAST, importTypeLabel, -10, SpringLayout.WEST, categoryComboBox);
        springLayout.putConstraint(SpringLayout.NORTH, importTypeLabel, 0, SpringLayout.NORTH, categoryComboBox);
        springLayout.putConstraint(SpringLayout.SOUTH, importTypeLabel, 0, SpringLayout.SOUTH, categoryComboBox);

        // 初始化类型简介
        typeDesTextArea = new JTextPane();
        typeDesTextArea.setEditable(false);
        typeDesTextArea.setDisabledTextColor(new JBColor(new Color(0, 0, 0, 0), new Color(0, 0, 0, 0)));
        typeDesTextArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        typeDesTextArea.setFont(typeDesTextArea.getFont().deriveFont(12f));
        springLayout.putConstraint(SpringLayout.EAST, typeDesTextArea, -5, SpringLayout.WEST, separatorVertical);
        springLayout.putConstraint(SpringLayout.WEST, typeDesTextArea, 20, SpringLayout.WEST, panel);
        springLayout.putConstraint(SpringLayout.NORTH, typeDesTextArea, 30, SpringLayout.SOUTH, typeList);
        panel.add(typeDesTextArea);

        // 初始化搜索框
        JBTextField searchTextField = new JBTextField();
        searchTextField.getEmptyText().setText(Constants.PLUGIN_SEARCH);
        searchTextField.getEmptyText().setFont(searchTextField.getEmptyText().getComponent().getFont().deriveFont(14f));
        searchTextField.setColumns(20);
        searchTextField.setMinimumSize(new Dimension(250, 40));
        springLayout.putConstraint(SpringLayout.EAST, searchTextField, -10, SpringLayout.EAST, panel);
        springLayout.putConstraint(SpringLayout.NORTH, searchTextField, -5, SpringLayout.NORTH, separatorVertical);
        // 搜索栏添加文字变化监听
        searchTextField.getDocument().addDocumentListener(new DocumentAdapter() {
            @Override
            protected void textChanged(@NotNull DocumentEvent documentEvent) {

            }

            @Override
            public void insertUpdate(@NotNull DocumentEvent e) {
                search(searchTextField.getText());
            }
        });
        // 添加删除按键监听
        searchTextField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE || e.getKeyCode() == KeyEvent.VK_DELETE) {
                    search(searchTextField.getText());
                }
            }
        });

        panel.add(searchTextField);

        // 测试按钮
        JButton testSetting = new JButton();
        testSetting.setText("设置仓库参数");
        springLayout.putConstraint(SpringLayout.EAST, testSetting, -20, SpringLayout.WEST, searchTextField);
        springLayout.putConstraint(SpringLayout.NORTH, testSetting, 0, SpringLayout.NORTH, separatorVertical);
        testSetting.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                SettingBean settingBean = new SettingBean();
                settingBean.setConfigUrl(localConfigUlr);
                settingBean.setOwner(localOwner);
                settingBean.setRepo(localRepo);
                settingBean.setRef(localRef);
                SettingDialog settingDialog = new SettingDialog(settingBean);
                settingDialog.setSettingResultListener((configUrl, owner, repo, ref) -> {
                    localConfigUlr = configUrl;
                    localOwner = owner;
                    localRepo = repo;
                    localRef = ref;
                    Constants.HOME_URL = String.format(Constants.BASE_URL, localOwner, localRepo);
                    requestData();
                });
                settingDialog.show();
            }
        });

        if (Constants.IS_DEBUG) {
            panel.add(testSetting);
        }
        // 案例简介
        desLabel = new JTextPane();
        desScrollPane = new JBScrollPane(desLabel);
        desScrollPane.setAutoscrolls(false);
        desScrollPane.setFocusable(false);
        desLabel.setAutoscrolls(false);
        desLabel.setFont(desLabel.getFont().deriveFont(13f));
        desLabel.setEditable(false);
        desLabel.setFocusable(false);
        desLabel.setDisabledTextColor(new JBColor(new Color(0, 0, 0, 0), new Color(0, 0, 0, 0)));
        desLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        desLabel.setPreferredSize(new Dimension(300, 100));
        springLayout.putConstraint(SpringLayout.WEST, desScrollPane, 0, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.EAST, desScrollPane, 0, SpringLayout.EAST, panel);
        springLayout.putConstraint(SpringLayout.SOUTH, desScrollPane, 0, SpringLayout.NORTH, separatorHorizontal);
        desLabel.setVisible(false);
        panel.add(desScrollPane);
        // 类型列表
        typeList.setSelectedIndex(0);
        dataList.clear();
        if (typeMap.containsKey(selectedCategory) && typeMap.get(selectedCategory).containsKey(selectedType)) {
            dataList.addAll(typeMap.get(selectedCategory).get(selectedType));
        }
        // 百度统计，需要用web组件打开网页才会有数据
        browser1 = new JBCefBrowser();
        @NotNull JComponent browserComponent1 = browser1.getComponent();
        browserComponent1.setPreferredSize(new Dimension(1, 1));
        springLayout.putConstraint(SpringLayout.WEST, browserComponent1, 0, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.NORTH, browserComponent1, 0, SpringLayout.SOUTH, searchTextField);
        panel.add(browser1.getComponent());
        // 百度统计
        browser2 = new JBCefBrowser();
        @NotNull JComponent browserComponent2 = browser2.getComponent();
        browserComponent2.setPreferredSize(new Dimension(1, 1));
        springLayout.putConstraint(SpringLayout.WEST, browserComponent2, 0, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.NORTH, browserComponent2, 0, SpringLayout.SOUTH, searchTextField);
        panel.add(browser2.getComponent());

        // 初始化组件/三方库列表
        componentPanel = new JPanel();
        componentPanel.setLayout(new WaterFlowLayout(Constants.WATER_FLOW_ITEM_WIDTH, Constants.WATER_FLOW_SPACING));
        // 滑动组件，需要将组件放在其中才能滑动
        jGridScrollPane = new JBScrollPane(componentPanel);
        springLayout.putConstraint(SpringLayout.WEST, jGridScrollPane, 0, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.EAST, jGridScrollPane, 0, SpringLayout.EAST, panel);
        springLayout.putConstraint(SpringLayout.NORTH, jGridScrollPane, 5, SpringLayout.SOUTH, searchTextField);
        springLayout.putConstraint(SpringLayout.SOUTH, jGridScrollPane, 0, SpringLayout.NORTH, desScrollPane);
        componentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        if (isDataLoadSuccess) {
            panel.add(jGridScrollPane);
        }

        // 初始化loading
        loadingPanel = new JPanel(new GridBagLayout());
        springLayout.putConstraint(SpringLayout.WEST, loadingPanel, 0, SpringLayout.EAST, separatorVertical);
        springLayout.putConstraint(SpringLayout.EAST, loadingPanel, 0, SpringLayout.EAST, panel);
        springLayout.putConstraint(SpringLayout.NORTH, loadingPanel, 0, SpringLayout.SOUTH, searchTextField);
        springLayout.putConstraint(SpringLayout.SOUTH, loadingPanel, 0, SpringLayout.NORTH, desScrollPane);
        loadingText = new JLabel();
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.CENTER;
        constraints.weightx = 1.0;
        constraints.weighty = 1.0;
        constraints.insets = JBUI.emptyInsets();
        loadingPanel.add(loadingText, constraints);
        if (!isDataLoadSuccess) {
            loadingText.setText("数据加载异常，请检查网络或者更新插件版本");
            panel.add(loadingPanel);
        }

        // 版本号
        String curVersion = VersionControlUtil.parseCurVersion();
        Map<String, String> latestVersionInfo = VersionControlUtil.parseLatestVersion();
        String latestVersion = latestVersionInfo.get("version");
        JLabel versionPane = new JLabel();
        versionPane.setText("v" + curVersion);
        springLayout.putConstraint(SpringLayout.WEST, versionPane, 10, SpringLayout.WEST, panel);
        springLayout.putConstraint(SpringLayout.SOUTH, versionPane, -10, SpringLayout.SOUTH, panel);
        panel.add(versionPane);

        if (VersionControlUtil.needUpdate(curVersion, latestVersion)) {
            JLabel updatePane = new JLabel();
            updatePane.setForeground(new Color(30, 144, 255));
            updatePane.setText(String.format("(latest version: %s)", latestVersion));
            updatePane.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    // 获得焦点并设置下划线
                    updatePane.setCursor(new Cursor(Cursor.HAND_CURSOR));
                    updatePane.setText(String.format("<html><u>(latest version: %s)</u></html>", latestVersion));
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    updatePane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    updatePane.setText(String.format("(latest version: %s)", latestVersion));
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getButton() == MouseEvent.BUTTON1) {
                        DownloadDialog.showDialog();
                    }
                }
            });
            springLayout.putConstraint(SpringLayout.WEST, updatePane, 0, SpringLayout.EAST, versionPane);
            springLayout.putConstraint(SpringLayout.SOUTH, updatePane, -10, SpringLayout.SOUTH, panel);
            panel.add(updatePane);

            SwingUtilities.invokeLater(() -> {
                boolean confirm = new RemindMessage(latestVersion).showAndGet();
                if (confirm) {
                    DownloadDialog.showDialog();
                }
            });
        }

        // 异步加载图片
        if (isDataLoadSuccess) {
            loadRepositoryList();
        }

        typeList.setSelectedIndex(0);

        return panel;
    }

    // 调用统计接口
    public void statistics(JBCefBrowser browser, String url) {
        if (!Constants.IS_DEBUG) {
            browser.loadURL(url);
            StatisticsUtils.loadBaiduTongjiScript(browser);
        }
    }

    // 加载案例数据
    private void loadRepositoryList() {
        if (imageLoaderWorker != null) {
            imageLoaderWorker.stop();
        }
        if (componentPanel != null) {
            componentPanel.removeAll();
            if (Objects.equals(selectedCategory, "case")) {
                componentPanel.setLayout(new WaterFlowLayout(Constants.WATER_FLOW_ITEM_WIDTH, Constants.WATER_FLOW_SPACING));
            }
            if (Objects.equals(selectedCategory, "library")) {
                componentPanel.setLayout(new WaterFlowLayout(800, Constants.WATER_FLOW_SPACING, 1));
            }
        }
        // 通过子线程下载图片，避免每次打开插件都重新加载
        imageLoaderWorker = new ImageLoaderWorker(componentPanel, dataList);
        imageLoaderWorker.setImageLoaderListener(new ImageLoaderListener() {
            @Override
            public void progress(int progress) {
//                loadingText.setText("loading... " + progress + "%");
                loadingText.setText(String.format(Constants.PLUGIN_LOADING_PROGRESS, progress));
            }

            @Override
            public void done() {
                desLabel.setVisible(true);
                jGridScrollPane.setVisible(true);
                loadingPanel.setVisible(false);
            }
        });
        imageLoaderWorker.execute();
    }

    // 获取分类列表
    private @NotNull JBList<TypeInfo> getTypeList() {
        defaultListModel = new DefaultListModel<>();
        defaultListModel.addAll(typeInfoList);
        JBList<TypeInfo> typeList = new JBList<>(defaultListModel);
        typeList.setPreferredSize(new Dimension(150, 200));
        // 使用自定义item
        typeList.setCellRenderer((jList, value, index, isSelected, cellHasFocus) -> {
            JLabel label = new JLabel(value.getTypeDesc());
            label.setFont(label.getFont().deriveFont(14f));
            label.setOpaque(true);
            label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            if (isSelected) {
                label.setBackground(typeList.getSelectionBackground());
                label.setForeground(typeList.getSelectionForeground());
            } else {
                label.setBackground(typeList.getBackground());
                label.setForeground(typeList.getForeground());
            }
            return label;
        });
        // 添加选中效果
        typeList.addListSelectionListener(listSelectionEvent -> {
            if (!listSelectionEvent.getValueIsAdjusting()) {
                if (!typeList.isEmpty()) {
                    if (typeList.getSelectedValue() != null) {
                        typeDesTextArea.setText(typeList.getSelectedValue().getTypeDetail());
                        selectedType = typeList.getSelectedValue().getTypeName();
                        if (imageLoaderWorker != null) {
                            dataList.clear();
                            dataList.addAll(typeMap.get(selectedCategory).get(selectedType));
                            loadingPanel.setVisible(true);
                            jGridScrollPane.setVisible(false);
                            desLabel.setVisible(false);
                            loadRepositoryList();
                        }
                    }
                }
            }
        });

        return typeList;
    }

    @Override
    public void doCancelAction() {
        super.doCancelAction();
        if (imageLoaderWorker != null) {
            imageLoaderWorker.stop();
        }
    }

    @Override
    protected void doOKAction() {

        // 如果未选择案例，则直接关闭Dialog
        if (selectPosition == -1) {
            super.doCancelAction();
            return;
        }
        if (codeListener != null) {
            toolWindowController.addTab(dataList.get(selectPosition).getName());
            if (selectedCategory.equals(Constants.PLUGIN_CATEGORY_CASE)) {
                doCaseDownload();
            }
            if (selectedCategory.equals(Constants.PLUGIN_CATEGORY_LIBRARY)) {
                doLibraryDownload();
            }
        }

    }

    // 根据文件夹名获取完整路径
    private String findPath(AnActionEvent event, String dirName) {
        String result = "";
        boolean isTargetDir = false;
        VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE);
        String temp0 = file.getPath();
        // 遍历路径，查找到项目根目录，并创建案例下载文件夹
        while (!isTargetDir) {
            if (file.isDirectory()) {
                if (file.findChild(dirName) == null) {
                    file = LocalFileSystem.getInstance().findFileByPath(temp0 = temp0.substring(0, temp0.lastIndexOf("/")));
                    isTargetDir = false;
                } else {
                    result = file.getPath();
                    isTargetDir = true;
                }

            } else {
                file = LocalFileSystem.getInstance().findFileByPath(temp0 = temp0.substring(0, temp0.lastIndexOf("/")));
                isTargetDir = false;
            }
        }
        return result;
    }

    // 下载三方库
    private void doLibraryDownload() {
        VirtualFile file = codeListener.getActionEvent().getData(CommonDataKeys.VIRTUAL_FILE);
        String localPath = file.getCanonicalPath();
        String modulePath = localPath.substring(0, localPath.indexOf("main") + 4) + "/module.json5";
        String rootPath = findPath(codeListener.getActionEvent(), "AppScope");
        List<ExtraInfo> extraInfos = dataList.get(selectPosition).getCodeInfo().getExtraInfos();
        boolean needSelectAbility = false;
        for (ExtraInfo extraInfo : extraInfos) {
            if (extraInfo.getFileType() == ETSMethodInfo.FileType.Ability.getValue()) {
                needSelectAbility = true;
                break;
            }
        }
        if (needSelectAbility) {
            // 弹出文件选择框，等待用户选择Ability文件路径
            String result = openFileChooser(codeListener.getActionEvent().getProject(), rootPath);

            if (result != null && !result.isEmpty()) {
                // 用户选择了文件或文件夹，获取选择的文件路径
                if (!result.endsWith("ets")) {
                    return;
                }
                codeListener.insertCode(selectedCategory, dataList.get(selectPosition), modulePath, rootPath, result);
            }
        } else {
            codeListener.insertCode(selectedCategory, dataList.get(selectPosition), modulePath, rootPath, "");
        }
        super.doOKAction();
    }

    // 下载源代码
    private void doCaseDownload() {
        String rootPath = findPath(codeListener.getActionEvent(), "AppScope");
        String selectPath = rootPath + "/" + Constants.CASE_SUB_DIR_NAME;
        List<ExtraInfo> extraInfos = dataList.get(selectPosition).getCodeInfo().getExtraInfos();
        boolean needSelectAbility = false;
        if (extraInfos != null && !extraInfos.isEmpty()) {
            for (ExtraInfo extraInfo : extraInfos) {
                if (extraInfo.getFileType() == ETSMethodInfo.FileType.Ability.getValue()) {
                    needSelectAbility = true;
                    break;
                }
            }
        }

        if (needSelectAbility) {
            // 弹出文件选择框，等待用户选择Ability文件路径
            String result = openFileChooser(codeListener.getActionEvent().getProject(), rootPath);

            if (result != null && !result.isEmpty()) {
                // 用户选择了文件或文件夹，获取选择的文件路径
                if (!result.endsWith("ets")) {
                    return;
                }
                startDownload(selectPath, rootPath, result);
            }
        } else {
            startDownload(selectPath, rootPath, "");
//            codeListener.insertCode(selectedCategory, dataList.get(selectPosition), "", rootPath);
        }

    }

    private void startDownload(String selectPath, String rootPath, String abilityPath) {
        // 获取被选择的案例的数据
        String[] temp = dataList.get(selectPosition).getCodeInfo().getCasePath().split("/");
        String dirName = temp[temp.length - 1];
        File file = new File(selectPath + "/" + dirName);
        // 当案例已经下载过，则提示用户覆盖代码
        if (file.exists() && file.isDirectory()) {
            ConfirmDialog confirmDialog = new ConfirmDialog("该操作将重新下载案例源代码，会覆盖已经修改过的代码，是否继续？");
            String finalSelectPath = selectPath;
            String finalRootPath = rootPath;
            confirmDialog.setConfirmListener(isOK -> {
                if (isOK) {
                    statistics(browser1, Constants.STATISTICS_COUNT);
                    statistics(browser2, String.format(Constants.STATISTICS_PRE, dataList.get(selectPosition).getCodeInfo().getCasePath()));
                    // 执行下载、插入代码操作
                    codeListener.insertCode(selectedCategory, dataList.get(selectPosition), finalSelectPath, finalRootPath, abilityPath);
                    super.doOKAction();
                }
            });
            confirmDialog.show();
        } else {
            statistics(browser1, Constants.STATISTICS_COUNT);
            statistics(browser2, String.format(Constants.STATISTICS_PRE, dataList.get(selectPosition).getCodeInfo().getCasePath()));
            codeListener.insertCode(selectedCategory, dataList.get(selectPosition), selectPath, rootPath, abilityPath);
            super.doOKAction();
        }
    }

    // 搜索
    public void search(String text) {
        desLabel.setText("");
        dataList.clear();
        List<RepositoriesInfo> allList = typeMap.get(selectedCategory).get(categoryMap.get(selectedCategory).get(0).getTypeName());
        for (RepositoriesInfo repositoriesInfo : allList) {
            if (repositoriesInfo.getName().toLowerCase().contains(text.toLowerCase())) {
                dataList.add(repositoriesInfo);
            }
        }
        loadRepositoryList();
    }

    // 定义一个 SwingWorker 用于异步加载图片
    public class ImageLoaderWorker extends SwingWorker<List<RepositoriesInfo>, RepositoriesInfo> {
        private final JPanel gridPanel;
        private List<RepositoriesInfo> list;
        private ImageLoaderListener imageLoaderListener;
        boolean stop = false;

        public ImageLoaderWorker(JPanel gridPanel, List<RepositoriesInfo> list) {
            this.gridPanel = gridPanel;
            this.list = list;
        }

        public void setImageLoaderListener(ImageLoaderListener imageLoaderListener) {
            this.imageLoaderListener = imageLoaderListener;
        }

        // 停止在进行的任务
        public void stop() {
            cancel(true);
            stop = true;
            if (gridPanel != null) {
                gridPanel.removeAll();
            }
        }

        public void setList(List<RepositoriesInfo> list) {
            this.list = list;
        }

        @Override
        protected List<RepositoriesInfo> doInBackground() {
            for (int i = 0; i < list.size(); i++) {
                if (!stop && !isCancelled()) {
                    RepositoriesInfo item = list.get(i);
                    String fileName = getFileNameFromUrl(item.getImage());
                    // 判断本地是否已经存在图片，不存在则重新下载，否则使用本地缓存图片
                    File localFile = new File(getCacheDirectory(), fileName);
                    if (!localFile.exists()) {
                        try {
                            // 加载图片
                            URL url = new URL(item.getImage());
                            InputStream inputStream = url.openStream();
                            Files.copy(inputStream, localFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                            item.setIcon(new ImageIcon(localFile.getAbsolutePath()));
                            // 将图片发布给 EDT 进行 UI 更新
                        } catch (Exception ignored) {

                        }
                    }
                    item.setLocalPath(localFile.getAbsolutePath());
                    item.setIcon(new ImageIcon(localFile.getAbsolutePath()));

                    publish(item);

                    if (imageLoaderListener != null) {
                        imageLoaderListener.progress(i * 100 / list.size());
                    }
                }
            }
            return null;
        }


        @Override
        protected void process(List<RepositoriesInfo> chunks) {
            if (stop) {
                return;
            }
            // 在 GridLayout 中显示已加载的图片
            for (RepositoriesInfo item : chunks) {
                WaterFlowItem gridItem = new WaterFlowItem(item, selectedCategory);
                // 案例选中事件
                gridItem.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mousePressed(MouseEvent e) {
                        if (item.getName().isEmpty() && item.getId().isEmpty() && item.getImage().isEmpty()) {
                            return;
                        }
                        // 被选中时请求焦点
                        e.getComponent().requestFocus();
                        for (Component otherItem : gridPanel.getComponents()) {
                            ((WaterFlowItem) otherItem).setSelected(false);
                        }
                        // 设置选中状态
                        gridItem.setSelected(true);
                        selectPosition = list.indexOf(item);
                        // 设置案例描述文字
                        desLabel.setText("");
                        SimpleAttributeSet simpleAttributeSet = new SimpleAttributeSet();
                        StyleConstants.setFontSize(simpleAttributeSet, 15);
                        StyleConstants.setForeground(simpleAttributeSet, JBColor.BLUE);
                        StyleConstants.setLineSpacing(simpleAttributeSet, 15);
                        StyledDocument styledDocument = desLabel.getStyledDocument();
                        try {
                            styledDocument.insertString(styledDocument.getLength(), list.get(selectPosition).getName() + "\n" + list.get(selectPosition).getDescription(), null);
                            styledDocument.setCharacterAttributes(0, list.get(selectPosition).getName().length(), simpleAttributeSet, false);
                        } catch (BadLocationException ex) {
                            throw new RuntimeException(ex);
                        }
                        // 需要放在SwingUI线程中执行，否则进度条无法滑动
                        SwingUtilities.invokeLater(() -> {
                            desScrollPane.getVerticalScrollBar().setValue(0);
                        });
                    }
                });
                gridPanel.add(gridItem);

            }

            ((WaterFlowLayout) (gridPanel.getLayout())).reset();
            gridPanel.revalidate();  // 重新布局
            gridPanel.repaint();     // 重新绘制
        }

        @Override
        protected void done() {
            super.done();
            if (imageLoaderListener != null) {
                imageLoaderListener.done();
            }
        }
    }
}